/* 
  Copyright (c) SilverLining 2015
  Date:    2016-1-7
  File:    m_safe.h  
  Description: 

*/

#ifndef _M_SAFE_H_
#define _M_SAFE_H_

/// <summary>
/// Provide a few "safe" reference initializers for creating, assigning,
/// and releasing reference counted objects. The caller still needs to 
/// provide the correct semantics (outlined below) to use these.
/// </summary>
/// <remarks>You should default initialize any value you intend to use here.</remarks>

/// <summary>
/// Release and clear an object at the given location if it has been defined.
/// </summary>
template <typename T>
void clear(T*& location)
{
	if (location)
	{
		location->release();
		location = nullptr;
	}
}
/// <summary>
/// Release and clear an object at the given location if it has been defined.
/// </summary>
void clear(wchar_t*& location);

/// <summary>
/// Assign a previously assigned object to the specified location.
/// </summary>
/// <remarks>If the location has already been defined, the reference will be released.</remarks>
template <typename T, typename Y>
T* assign(T*& location, Y* ptr)
{
	if ((void*)location != (void*)ptr)
	{
		clear<T>(location);
		location = ptr;
		if (location)
		{
			location->reference();
		}
	}
	return location;
}

/// <summary>
/// Assign a null terminated string to the given location.
/// </summary>
/// <remarks>If the location has already been defined, the reference will be released.</remarks>
wchar_t* assign(wchar_t*& location, const wchar_t* value);

/// <summary>
/// Assign a newly created object to a specified location.
/// </summary>
/// <remarks>If the location has already been defined, the reference will be released.</remarks>
template <typename T, typename Y>
T* assign_new(T*& location, Y* ptr)
{
	if ((void*)location != (void*)ptr)
	{
		clear<T>(location);
		location = ptr;
	}
	return location;
}

/// <summary>
/// Swap two objects.
/// </summary>
/// <remarks>Assigning any reference counted object has the potential of leaking memory if you don't check the identity of the objects being referenced first.</remarks>
template <typename T, typename Y>
void swap(T*& left, Y*& right)
{
	if ((void*)left != (void*)right)
	{
		ref_storage track1;
		T* temp = create(track1, left);
		assign(left, right);
		assign(right, temp);
	}
}

/// <summary>
/// Provide stack-managed temporary reference storage for a given object.
/// </summary>
class ref_storage
{
public:
	ref_storage();
	ref_storage(object* ptr);
	~ref_storage();

	ref_storage& operator=(ref_storage& other);
	/// <summary>
	/// Clear the storage. This can only be done once.
	/// </summary>
	void clear();

	object* _Ptr;

private:
	bool _Assigned;
};

/// <summary>
/// Get a weak reference to an object.
/// </summary>
/// <returns>The weak reference to the object.</returns>
template <typename T>
weak_ref* create_weak(ref_storage& ss, T* obj)
{
	weak_ref* ret = nullptr;
	if (obj)
	{
		ret = obj->reference_weak();
	}		
	ss = ref_storage(ret);
	return ret;
}

/// <summary>
/// Wrap an object in safe storage in the case that it's being constructed.
/// </summary>
/// <returns>A reference to the object being created.</returns>
template <typename T>
T* create(ref_storage& ss, T* newObject)
{
	ss = ref_storage(newObject);
	return newObject;
}

/// <summary>
/// Temporarily reference an object in scope.
/// </summary>
/// <remarks>This can be useful in multi-threaded code where multiple callers may be working with the same temporary object.</remarks>
template <typename T>
T* reference(ref_storage& ss, T* obj)
{
	obj->reference();
	ss = ref_storage(obj);
	return obj;
}

/// <summary>
/// Return the value of previously created object.
/// </summary>
/// <remarks>As a side effect, this also clears the callers reference.</remarks>
template <typename T>
T* value(ref_storage& ss, T* obj)
{
	assert(ss._Ptr == obj);
	if (ss._Ptr == obj)
	{
		ss.clear();
		return obj;
	}
	return nullptr;
}

/// <summary>
/// Wrap a weak reference instance in safe storage in the case that it's being locked.
/// </summary>
/// <returns>A strong object reference for the weak reference, if one could be obtained.</returns>
template <typename T>
T* lock(ref_storage& ss, weak_ref* ptr)
{
	if (ptr)
	{
		object* obj = ptr->lock();
		if (obj)
		{
			ss = ref_storage(obj);
			T* ret = dynamic_cast<T*>(obj);
			if (ret)
			{
				return ret;
			}
		}
		else
		{
			ss = ref_storage(nullptr);
		}
	}
	return nullptr;
}

#endif // _M_SAFE_H_
